/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JPanel;
import phys.CollisionResolver;
import phys.RK4;
import phys.Time;
import spring.Ball;
import spring.Block;
import spring.Element;
import spring.Gravity;
import spring.Spring;
import vec.Vector3f;

/**
 *
 * @author Vic
 */
public class PhysicsPanel extends JPanel {

    final static Vector3f origen = new Vector3f(400, 400, 0);
    final ArrayList<Element> Elements = new ArrayList<>();
    final Ball ball = new Ball(origen, new Rectangle((int) (origen.x - 800), (int) (origen.y - 800), 800, 800));
    final static AtomicBoolean adjusting = new AtomicBoolean();
    final public static float dt = 1 / 1024f;

    static Vector3f toScreenSpace(Vector3f modelspace) {
        Vector3f point = new Vector3f(modelspace);
        point.y = origen.y * 2 - point.y;
        return point;
    }

    static Vector3f toModelSpace(Vector3f screenspace) {
        Vector3f point = new Vector3f(screenspace);
        point.y = origen.y * 2 - point.y;
        return point;
    }

    public PhysicsPanel() {
        Elements.add(ball);
        initPanel();
        CollisionResolver.setBounds(getBounds());
    }

    private void initPanel() {
        MouseAdapter ma = new MouseAdapter() {
            int button = 0;

            @Override
            public void mouseDragged(MouseEvent e) {
                Vector3f point = toModelSpace(new Vector3f(e.getX(), e.getY(), 0));
                synchronized (Elements) {
                    if (button == MouseEvent.BUTTON1) {
                        adjusting.set(true);
                        for (Element s : Elements) {
                            if (s.collides(point)) {
                                adjusting.set(true);
                                s.setPosition(new Vector3f(point.x, point.y,0));
                            }
                        }
                    }
                }
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                Vector3f point = toModelSpace(new Vector3f(e.getX(), e.getY(), 0));
                super.mouseReleased(e);
                if (e.isPopupTrigger()) {
                    popTrigger(e);
                } else if (!adjusting.get()) {
                    Block b = new Block(new Rectangle((int) point.x, (int) point.y, 100, 100));
                    Elements.add(b);
                }
                adjusting.set(false);
            }

            @Override
            public void mousePressed(MouseEvent e) {
                button = e.getButton();

                if (e.isPopupTrigger()) {
                    popTrigger(e);
                } else {

                    mouseMoved(e);
                }
            }

            void popTrigger(MouseEvent e) {
                Vector3f point = toModelSpace(new Vector3f(e.getX(), e.getY(), 0));
                synchronized (Elements) {

                    Element toRemove = null;
                    for (Element s : Elements) {
                        if (s.collides(point)) {
                            adjusting.set(true);
                            toRemove = s;
                            break;
                        }
                    }
                    if (toRemove != null) {
                        ball.removeForce(toRemove);
                        Elements.remove(toRemove);
                    } else {
                        switch (e.getModifiersEx() & (MouseEvent.CTRL_DOWN_MASK | MouseEvent.ALT_DOWN_MASK)) {
                            case MouseEvent.CTRL_DOWN_MASK:
                                Gravity grav = new Gravity(-4e8f, 0);
                                grav.getState().getPosition().x = point.x;
                                grav.getState().getPosition().y = point.y;
                                Elements.add(grav);
                                ball.addForce(grav);
                                break;
                            case MouseEvent.ALT_DOWN_MASK:
                                grav = new Gravity(4e8f, 0);
                                grav.getState().getPosition().x = point.x;
                                grav.getState().getPosition().y = point.y;
                                Elements.add(grav);
                                ball.addForce(grav);
                                break;
                            case 0:
                                Spring spring = new Spring(50, 200, 1, new Vector3f(point.x, point.y, 0));
                                Elements.add(spring);
                                ball.addForce(spring);
                                spring.setState(ball.getState());
                                break;
                        }
                    }
                }
            }
        };


        addMouseListener(ma);
        addMouseMotionListener(ma);
    }

    public static void Loop() throws InterruptedException {
        MainFrame mFrame = new MainFrame();
        mFrame.show();

        CollisionResolver.setBounds(mFrame.physicsPanel.getBounds());
        mFrame.createBufferStrategy(2);
        BufferStrategy bufferStrategy = mFrame.getBufferStrategy();
        float startTime = System.nanoTime() / 1e9f;
        float lastTime = 0;
        float[] remaining = {0}, currentTime = {0};
        while (true) {
            currentTime[0] = System.nanoTime() / 1e9f - startTime;
            Graphics2D drawGraphics = (Graphics2D) bufferStrategy.getDrawGraphics();
            drawGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            mFrame.paint(drawGraphics);

            if (!adjusting.get()) {
                float frameTime = (float) ((currentTime[0] - lastTime));
                remaining[0] += frameTime * Math.pow(mFrame.timeSlider.getValue() / 50.0, 1.5);
                if (frameTime > 0) {
                    Time.updateAll((float) (2 * frameTime * Math.pow(mFrame.timeSlider.getValue() / 50.0, 1.1)));

                    while (remaining[0] - dt > 0) {
                        mFrame.physicsPanel.updatePos(remaining, currentTime);
                        if (mFrame.collisionButton.isSelected()) {
                            CollisionResolver.resolve();
                        }
                    }
                }
            }
            lastTime = currentTime[0];
            bufferStrategy.show();
            drawGraphics.dispose();
        }

    }

    public void updatePos(float[] remaining, float[] currentTime) {
        synchronized (Elements) {
            RK4.integrate(ball, currentTime[0], dt);
            remaining[0] -= dt;
            currentTime[0] += dt;
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        Color back = getBackground();

//        g.setColor(new Color(0x0F000000 | back.getRGB()));
//        g.fillRect(0, 0, getWidth(), getHeight());
//        g.setColor(new Color(0));
//        g.fillRect(0, 0, 800, 800);
        synchronized (Elements) {
            for (Element s : Elements) {
                s.draw(g, origen);
            }
        }
    }
}
